﻿/******************************************************************************
 * GNewsSearch.cs
 * 
 * This module implements the GNewsSearch search class and related types.
 *  
 * Date:   12/2008
 * 
 * Copyright (c) 2009, 2010, Mark Betz 
 * 
 * All rights reserved. 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met: 
 * 
 *   * Redistributions of source code must retain the above copyright 
 *     notice, this list of conditions and the following disclaimer. 
 *   * Redistributions in binary form must reproduce the above copyright 
 *     notice, this list of conditions and the following disclaimer in the 
 *     documentation and/or other materials provided with the distribution. 
 *   * Neither the name of the Author nor the names of contributors may be 
 *     used to endorse or promote products derived from this software
 *     without specific prior written permission. 
 *     
 * THIS SOFTWARE IS PROVIDED BY MARK BETZ ''AS IS'' AND ANY  EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
 * IN NO EVENT SHALL MARK BETZ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 * 
 */
using System;

namespace GSearch
{
    /// <summary>
    /// Type of the event arguments to GNewsSearch events
    /// </summary>
    public class NewsSearchEventArgs : SearchEventArgs
    {
        /// <summary>
        /// Constructor used to initialize from a SearchEventArgs object.
        /// </summary>
        /// <param name="se"></param>
        public NewsSearchEventArgs(SearchEventArgs se)
            : base(se.Status, se.Result, se.Ex)
        {
        }

        /// <summary>
        /// Returns a reference to a <see cref="GSearch.GNewsResponse"/> object
        /// that contains the response, for certain event types
        /// </summary>
        public new GNewsResponse Result
        { get { return (GNewsResponse)(base.Result); } }
    }
    
    /// <summary>
    /// Enumerates a set of topic scope values that can be used
    /// to scope a news search - new feature and this is not the
    /// full list of supported topics in all news editions.
    /// </summary>
    public enum SearchNewsTopics
    {
        /// <summary>
        /// Selects all news stories regardless of topical category
        /// </summary>
        All,
        
        /// <summary>
        /// Selects news stories from the top headlines
        /// </summary>
        TopHeadlines,

        /// <summary>
        /// Selects news stories from the world category
        /// </summary>
        World,

        /// <summary>
        /// Selects news stories from the business category
        /// </summary>
        Business,

        /// <summary>
        /// Selects news stories from the nation category
        /// </summary>
        Nation,

        /// <summary>
        /// Selects news stories from the science and technology category
        /// </summary>
        ScienceTech,

        /// <summary>
        /// Selects news stories from the elections category
        /// </summary>
        Elections,

        /// <summary>
        /// Selects news stories from the politics category
        /// </summary>
        Politics,

        /// <summary>
        /// Selects news stories from the entertainment category
        /// </summary>
        Entertainment,

        /// <summary>
        /// Selects news stories from the sports category
        /// </summary>
        Sports,

        /// <summary>
        /// Selects news stories from the health category
        /// </summary>
        Health
    };

    /// <summary>
    /// Enumerates the international editions available. This is a
    /// manually-harvested list and may not be up-to-date.
    /// </summary>
    public enum SearchNewsEdition
    {
        UnitedStates,
        UnitedStatesSpanish,
        Argentina,
        Australia,
        Belgium,
        BelgiumFrench,
        Botswana,
        Brasil,
        Canada,
        CanadaFrench,
        Chile,
        Columbia,
        Cuba,
        CzechRepublic,
        Germany,
        Spain,
        Ethiopia,
        France,
        Ghana,
        IndiaEnglish,
        Ireland,
        Italy,
        Kenya,
        Hungary,
        Mexico,
        Malaysia,
        Namibia,
        Netherlands,
        NewZealand,
        Nigeria,
        Norway,
        Austria,
        Pakistan,
        Peru,
        Philippines,
        Poland,
        Portugal,
        SwitzerlandGerman,
        SwitzerlandFrench,
        Singapore,
        SouthAfrica,
        Sweden,
        Tanzania,
        Turkey,
        Uganda,
        UnitedKingdom,
        Venezuela,
        Zimbabwe,
        China,
        HongKong,
        Japan,
        Korea,
        Taiwan,
        Israel,
        Greece,
        Arabic,
        Russia,
        IndiaHindi,
        IndiaTamil,
        IndiaMalayalam,
        IndiaTelugu
    };

    /// <summary>
    /// NewsSearchArgs derives from <see cref="GSearch.SearchArgs"/> and implements
    /// news search-specific arguments.
    /// <example>
    /// The following code illustrates how to create and use the NewsSearchArgs class:
    /// <code>
    /// GNewsSearch gns = new GNewsSearch();
    /// NewsSearchArgs nsa = new NewsSearchArgs();
    /// nsa.Terms = "US election";
    /// GNewsResponse resp = gns.Search(nsa);
    /// </code>
    /// </example>
    /// </summary>
    public class NewsSearchArgs : SearchArgs
    {
        /// <summary>
        /// Default constructor for the NewsSearchArgs class.
        /// </summary>
        public NewsSearchArgs()
        {
            Scoring = SearchScoring.Relevance;
            Location = "";
            QuotedPersonId = "";
            Topic = SearchNewsTopics.All;
            Edition = SearchNewsEdition.UnitedStates;
        }

        /// <summary>
        /// ToQueryString overrides the base class version. It calls the
        /// base class version to get the root of the query string, and then
        /// builds the rest of the parameters.
        /// </summary>
        /// <returns></returns>
        public override string ToQueryString()
        {
            string qry = base.ToQueryString();

            // scoring=d sets the result ranking to date
            if (SearchScoring.Relevance != Scoring)
                qry += "&scoring=d";
            
            // geo= string specifiying location/scope by zip or city,state
            if ( "" != Location && null != Location )
                qry += "&geo=" + Uri.EscapeDataString(Location);
                
            // qsid= ID of well-quoted person, experimental feature
            if ( "" != QuotedPersonId && null != QuotedPersonId )
                qry += "&qsid=" + QuotedPersonId;
                
            // topic= topic scope
            if ( SearchNewsTopics.All != Topic )
                qry += "&topic=" + ArgStrings.topicType[Convert.ToInt32(Topic)];
                
            // ned= edition
            qry += "&ned=" + ArgStrings.newsEdition[Convert.ToInt32(Edition)];

            return qry;
        }
        
        /// <summary>
        /// A value from the <see cref="GSearch.SearchScoring"/> enum giving
        /// the ranking algorithm that should be applied to search results.
        /// </summary>
        public SearchScoring Scoring { get; set; }
        
        /// <summary>
        /// A System.String containing either a city,state combination or a
        /// zip code giving the location around which the search should be
        /// scoped. No checks are performed on the content prior to executing
        /// the search. Search terms are optional if this property is
        /// specified.
        /// </summary>
        public string Location { get; set; }
        
        /// <summary>
        /// A System.String containing the ID of a well-known quoted person
        /// who should serve as the topic of the search. Search terms are
        /// optional if this property is specified. This is an experimental
        /// feature. See the Google API documentation for information on
        /// quoted person IDs.
        /// </summary>
        public string QuotedPersonId { get; set; }
        
        /// <summary>
        /// A value from the <see cref="GSearch.SearchNewsTopics"/> enum giving
        /// the topic category around which the search should be scoped. Search
        /// terms are optional if this property is set to anything other than
        /// All.
        /// </summary>
        public SearchNewsTopics Topic { get; set; }
        
        /// <summary>
        /// A value from the <see cref="GSearch.SearchNewsEdition"/> enum giving
        /// the international news category in which the search should be
        /// performed. Defaults to UnitedStates.
        /// </summary>
        public SearchNewsEdition Edition { get; set; }
    }

    /// <summary>
    /// The GNewsSearch class provides an interface for searching news using Google.
    /// It derives from <see cref="GSearch.GSearchCore"/> which does a large part of
    /// the work.
    /// <example>
    /// The following example shows how to use the GNewsSearch class to search the news
    /// using Google:
    /// <code>
    /// GNewsSearch gns = new GNewsSearch();
    /// NewsSearchArgs nsa = new NewsSearchArgs();
    /// nsa.Terms = "US election";
    /// GNewsResponse resp = gns.Search(nsa);
    /// if ( resp.Response.Results.Length > 0 )
    ///     // process results
    /// </code>
    /// </example>
    /// </summary>
    public class GNewsSearch : GSearchCore
    {
#if (!SILVERLIGHT)
        /// <summary>
        /// Performs a syncronous search for news using the supplied arguments. The
        /// calling thread blocks until results are returned or an exception is thrown.
        /// This method is not available in the Silverlight version of the libraries.
        /// </summary>
        /// <param name="args"><see cref="GSearch.NewsSearchArgs"/> containing the search arguments</param>
        /// <returns><see cref="GSearch.GNewsResponse"/> containing the results</returns>
        /// <exception cref="Exception">if an error occurs during the search</exception>
        public GNewsResponse Search(NewsSearchArgs args)
        {
            return (GNewsResponse)base.Search(args);
        }
#endif

        /// <summary>
        /// Peforms an asyncronous search for news using the supplied arguments. The
        /// calling thread returns immediately, and results or errors are indicated by
        /// raising the SearchFailed, SearchComplete, or SearchProgressChanged events.
        /// Results are returned on the UI thread.
        /// </summary>
        /// <param name="args"><see cref="GSearch.NewsSearchArgs"/> containing the search arguments</param>
        /// <exception cref="Exception">if an error occurs during the search</exception>
        public void SearchAsync(NewsSearchArgs args)
        {
            base.SearchAsync(args);
        }

        /// <summary>
        /// This event is raised when the search is complete and the results
        /// are ready to be returned to the client.
        /// </summary>
        public event EventHandler<NewsSearchEventArgs> SearchComplete;

        /// <summary>
        /// This event is raised when the search fails for some reason. The event
        /// arguments Ex property will reference the exception.
        /// </summary>
        public event EventHandler<NewsSearchEventArgs> SearchFailed;

        /// <summary>
        /// This event is raised when key steps in the search process are
        /// completed. See the SearchStatus enumeration for possible values 
        /// for the NewsSearchEventArgs.Status parameter.
        /// <seealso cref="GSearch.SearchStatus"/>
        /// </summary>
        public event EventHandler<NewsSearchEventArgs> SearchProgressChanged;

        /// <summary>
        /// A System.String containing a complete URI for the news search service
        /// </summary>
        public override string ServiceURI
        {
            get { return base.ServiceURI + NewsResources.ServiceURI; }
        }

        /// <summary>
        /// Called by GSearchCore to raise the SearchComplete event
        /// </summary>
        /// <param name="e"><see cref="SearchEventArgs"/> containing 
        /// information about the event</param>
        protected override void OnSearchComplete(SearchEventArgs e)
        {
            EventHandler<NewsSearchEventArgs> ev = SearchComplete;
            if (null != ev)
            {
                NewsSearchEventArgs ie = new NewsSearchEventArgs(e);
                ev(this, ie);
            }
        }

        /// <summary>
        /// Called by GSearchCore to raise the SearchFailed event
        /// </summary>
        /// <param name="e"><see cref="SearchEventArgs"/> containing 
        /// information about the event</param>
        protected override void OnSearchFailed(SearchEventArgs e)
        {
            EventHandler<NewsSearchEventArgs> ev = SearchFailed;
            if (null != ev)
            {
                NewsSearchEventArgs ie = new NewsSearchEventArgs(e);
                ev(this, ie);
            }
        }

        /// <summary>
        /// Called by GSearchCore to raise the SearchProgressChanged event
        /// </summary>
        /// <param name="e"><see cref="SearchEventArgs"/> containing 
        /// information about the event</param>
        protected override void OnSearchProgressChanged(SearchEventArgs e)
        {
            EventHandler<NewsSearchEventArgs> ev = SearchProgressChanged;
            if (null != ev)
            {
                NewsSearchEventArgs ie = new NewsSearchEventArgs(e);
                ev(this, ie);
            }
        }

        /// <summary>
        /// Deserialize is called from the base when the search returns a valid
        /// result stream, it refers the call to the base class DeserializeImpl
        /// which actually does the work of deserialization.
        /// </summary>
        /// <param name="result">Stream result passed from the base class</param>
        /// <returns>Instance of <see cref="GSearch.GNewsResponse"/> as GResponse</returns>
        protected override GResponse Deserialize(System.IO.Stream result)
        {
            return DeserializeImpl<GNewsResponse>(result);
        }
    }
}
